﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Data;
using AutoMapper;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.ViewModel;
using Microsoft.Practices.ServiceLocation;
using Sautom.ClientComunication;
using Sautom.ClientComunication.CommandService;
using Sautom.ClientComunication.Events;
using Sautom.ClientComunication.QueriesService;
using Sautom.ClientComunication.Services;
using Sautom.Modules.Proposal.Controls;
using Sautom.Modules.Proposal.Models;

namespace Sautom.Modules.Proposal.ViewModels
{
    public sealed class ProposalEditViewModel : NavigationAwareNotificationObject
    {
        #region Constructor
        public ProposalEditViewModel(IRegionManager regionManager, IEventAggregator eventAggregator)
        {
            RegionManager = regionManager;

            SaveProposalCommand = new DelegateCommand<string>(ExecuteSaveProposalCommand, (param) => true);
            CourcesComboChanged = new Action<AutoComplete.AutoCompleteArgs>(
                args => ComboChangedActivated(args, GetCourseNameVariants));
            SchoolesComboChanged = new Action<AutoComplete.AutoCompleteArgs>(
                args => ComboChangedActivated(args, GetSchoolNameVariants));
            
            QueriesServiceClient = ServiceFactory.Get(eventAggregator).GetQueriesService();
            CommandsServiceClient = ServiceFactory.Get(eventAggregator).GetCommandsService();
        }

        #endregion

        #region Properties
        private IRegionManager RegionManager { get; set; }
        private IQueriesService QueriesServiceClient { get; set; }
        private ICommandService CommandsServiceClient { get; set; }

        #endregion

        #region Commands
        public DelegateCommand<string> SaveProposalCommand { get; private set; }
        public Action<AutoComplete.AutoCompleteArgs> CourcesComboChanged { get; private set; }
        public Action<AutoComplete.AutoCompleteArgs> SchoolesComboChanged { get; private set; }

        private static void ComboChangedActivated(AutoComplete.AutoCompleteArgs args, Func<string, IEnumerable<string>> dataGetAction)
        {
            if (string.IsNullOrEmpty(args.Pattern) || args.Pattern.Length < 3)
                args.CancelBinding = true;
            else
                args.DataSource = dataGetAction(args.Pattern);
        }

        private IEnumerable<string> _courceNames;
        private string _courceNameLastLoadedPattern;
        private IEnumerable<string> GetCourseNameVariants(string pattern)
        {
            if (_courceNames == null || !pattern.StartsWith(_courceNameLastLoadedPattern))
            {
                _courceNames = QueriesServiceClient.GetCourceNames(pattern);
                _courceNameLastLoadedPattern = pattern;
            }

            return _courceNames.Where(rec => rec.ToLower().StartsWith(pattern.ToLower()));
        }

        private IEnumerable<string> _schoolNames;
        private string _schoolNameLastLoadedPattern;
        private Guid _schoolNameLastLoadedCity;
        private IEnumerable<string> GetSchoolNameVariants(string pattern)
        {
            if (_schoolNames == null || !pattern.StartsWith(_schoolNameLastLoadedPattern) || SelectedCity.Id != _schoolNameLastLoadedCity)
            {
                _schoolNames = QueriesServiceClient.GetSchoolNames(pattern, SelectedCity.Id);
                _schoolNameLastLoadedPattern = pattern;
                _schoolNameLastLoadedCity = SelectedCity.Id;
            }

            return _schoolNames.Where(rec => rec.ToLower().StartsWith(pattern.ToLower()));
        }

        private void ExecuteSaveProposalCommand(string isSave)
        {
            if (isSave == "true")
            {
                Mapper.CreateMap<ProposalEditViewModel, ProposalEditDtoInput>()
                    .ForMember(d => d.HouseTypes, o => o.Ignore())
                    .ForMember(d => d.Intensities, o => o.Ignore());

                var proposalData = Mapper.Map<ProposalEditDtoInput>(this);
                proposalData.CityId = SelectedCity.Id;
                proposalData.Intensities = IntensivityList.Where(rec => rec.IsSelected).Select(rec => rec.Id).ToArray();
                proposalData.HouseTypes = HouseTypeList.Where(rec => rec.IsSelected).Select(rec => rec.Id).ToArray();
                
                if (!IsGroupType)
                {
                    proposalData.EndDate = null;
                    proposalData.StartDate = null;
                    proposalData.CuratorName = null;
                }

                CommandsServiceClient.ProposalEditOrAdd(proposalData);
            }

            RegionManager.RequestNavigate(RegionProvider.MainRegion, PathProvider.ProposalIndex);
        }

        #endregion

        #region INavigationAware implementation
        public override void OnNavigatedTo(NavigationContext navigationContext)
        {
            Countries = new ObservableCollection<CountryWitCitiesDtoOutput>(QueriesServiceClient.GetAllCountriesWithCities());
            SelectedCountry = Countries.First();
            SelectedCity = SelectedCountry.Cities.First();

            // Initial load - Load based on ID passed in
            string id = navigationContext.Parameters["proposalId"] ?? Guid.Empty.ToString();

            ProposalEditDtoOutput proposal = QueriesServiceClient.GetProposalForEdit(Guid.Parse(id));
            if (proposal.Id != Guid.Empty)
            {
                Mapper.CreateMap<ProposalEditDtoOutput, ProposalEditViewModel>();
                Mapper.Map(proposal, this);

                SelectedCountry = Countries.First(rec => rec.Id == proposal.CountryId);
                SelectedCity = SelectedCountry.Cities.First(rec => rec.Id == proposal.CityId);
            }

            IntensivityList = new ObservableCollection<GuidStringSelected>(proposal.AvailableIntensities.Select(
                    rec => new GuidStringSelected() { Id = rec.Id, Value = rec.Element, IsSelected = proposal.Intensities.Any(i => i == rec.Id), SelectionGroup = "IntensivityList" }));

            HouseTypeList = new ObservableCollection<GuidStringSelected>(proposal.AvailableHouseTypes.Select(
                    rec => new GuidStringSelected() { Id = rec.Id, Value = rec.Element, IsSelected = proposal.HouseTypes.Any(i => i == rec.Id), SelectionGroup = "HouseTypeList" }));
        }
        #endregion

        #region Proposal properties

        public Guid Id { get; set; }

        public ObservableCollection<CountryWitCitiesDtoOutput> Countries { get; set; }
        private CountryWitCitiesDtoOutput _selectedCountry;
        public CountryWitCitiesDtoOutput SelectedCountry
        {
            get { return _selectedCountry; }
            set
            {
                _selectedCountry = value;
                RaisePropertyChanged(() => SelectedCountry);

                SelectedCity = SelectedCountry.Cities.First();
                RaisePropertyChanged(() => SelectedCity);
            }
        }

        private CityItemDtoOutput _selectedCity;
        public CityItemDtoOutput SelectedCity
        {
            get { return _selectedCity; }
            set
            {
                _selectedCity = value;
                RaisePropertyChanged(() => SelectedCity);
            }
        }

        private string _schoolName;
        public string SchoolName
        {
            get { return _schoolName; }
            set
            {
                _schoolName = value;
                RaisePropertyChanged(() => SchoolName);
            }
        }

        private string _courseName;
        public string CourseName
        {
            get { return _courseName; }
            set
            {
                _courseName = value;
                RaisePropertyChanged(() => CourseName);
            }
        }

        private ObservableCollection<GuidStringSelected> _intensivityList;
        public ObservableCollection<GuidStringSelected> IntensivityList
        {
            get { return _intensivityList; }
            set
            {
                _intensivityList = value;
                RaisePropertyChanged(() => IntensivityList);
            }
        }

        private ObservableCollection<GuidStringSelected> _houseTypeList;
        public ObservableCollection<GuidStringSelected> HouseTypeList
        {
            get { return _houseTypeList; }
            set
            {
                _houseTypeList = value;
                RaisePropertyChanged(() => HouseTypeList);
            }
        }

        private bool _isGroupType;
        public bool IsGroupType
        {
            get { return _isGroupType; }
            set
            {
                _isGroupType = value;
                UpdateGroupTypeDependencies(value);
                RaisePropertyChanged(() => IsGroupType);
            }
        }

        private void UpdateGroupTypeDependencies(bool value)
        {
            if (value)
            {
                if (IntensivityList != null && HouseTypeList != null)
                {
                    foreach (var item in IntensivityList)
                    {
                        item.IsSelected = false;
                    }

                    IntensivityList.First().IsSelected = true;

                    foreach (var item in HouseTypeList)
                    {
                        item.IsSelected = false;
                    }
                    HouseTypeList.First().IsSelected = true;
                }
            }
        }

        private string _curatorName;
        public string CuratorName
        {
            get { return _curatorName; }
            set
            {
                _curatorName = value;
                RaisePropertyChanged(() => CuratorName);
            }
        }

        private DateTime _startDate;
        public DateTime StartDate
        {
            get
            {
                if (_startDate == default(DateTime))
                {
                    _startDate = DateTime.Now;
                    RaisePropertyChanged(() => StartDate);
                }

                return _startDate;
            }
            set
            {
                _startDate = value;
                RaisePropertyChanged(() => StartDate);
            }
        }

        private DateTime _endDate;
        public DateTime EndDate
        {
            get
            {
                if (_endDate == default(DateTime))
                {
                    _endDate = DateTime.Now;
                    RaisePropertyChanged(() => StartDate);
                }

                return _endDate;
            }
            set
            {
                _endDate = value;
                RaisePropertyChanged(() => EndDate);
            }
        }

        #endregion
    }
}